package com.helwen.config;


import com.helwen.constant.Constants;
import com.helwen.entity.UserDto;
import com.helwen.utils.ServletUtils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @author xay
 * @version 1.0  拦截mybatis操作数据库sql,在设置参数时，处理用户类型设置。
 * @since 2020/11/12
 */
@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)
public class MybatisCityInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object arg = invocation.getArgs()[1];
        Optional<UserDto> userDto = ServletUtils.safeCurrentUser();
        if(null == arg){
            Map<String,Object> paramMap = new MapperMethod.ParamMap<>();
            invocation.getArgs()[1] = paramMap;
            userDto.ifPresent(u -> {
                boolean isSys = Objects.equals(u.getUserType(), Constants.USER_TYPE_SYS);
                paramMap.put("city",isSys?null: u.getTenantId());
                paramMap.put("warehouseId",isSys?null:u.getWarehouseId());
            });
        }else if(arg instanceof Map){
            Map<String,Object> paramMap = (Map<String, Object>) invocation.getArgs()[1];
            userDto.ifPresent(u -> {
                String userType = u.getUserType();
                boolean isSys = Objects.equals(userType, Constants.USER_TYPE_SYS);
                paramMap.put("city",isSys?null: u.getTenantId());
                paramMap.put("warehouseId",isSys?null:u.getWarehouseId());
            });
        }else{
            MetaObject metaObject = SystemMetaObject.forObject(arg);
            userDto.ifPresent(a->{
                String userType = a.getUserType();
                boolean isSys = Objects.equals(userType, Constants.USER_TYPE_SYS);
                if(metaObject.hasSetter("city")&& Objects.equals(metaObject.getSetterType("city"),String.class)){
                    metaObject.setValue("city",isSys?null:a.getTenantId());
                }
                if(metaObject.hasSetter("warehouseId")&& Objects.equals(metaObject.getSetterType("warehouseId"),Long.class)){
                    metaObject.setValue("warehouseId",isSys?null:a.getWarehouseId());
                }
            });
        }
        return invocation.proceed();
    }

    public Object plugin(Object target) {
        if (target instanceof Executor || target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }
}
